﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Resources;
using System.Windows.Shapes;
using System.Collections.Specialized;

namespace TTApp.Phone.Silverlight.Hexagons
{
    /// <summary>
    /// Chứa các Decoration và Hexagon.
    /// </summary>
    [ContentProperty("Hexagon")]
    public partial class HexagonSlot : Grid
    {
        #region //----------------------------- Class Private Fields --------------------------------------//
        private DecorationCollection _decorations;
        private Dictionary<string, Decoration> _decorationDictionary;
        #endregion

        #region //----------------------------- Dependency Properties -------------------------------------//
        public static readonly DependencyProperty BackgroundImageProperty;
        public static readonly DependencyProperty HexagonProperty;
        #endregion

        #region //----------------------------- Public Event Delegates ------------------------------------//
        public event RoutedPropertyChangedEventHandler<Image> BackgroundImageChanged;
        public event RoutedPropertyChangedEventHandler<Hexagon> HexagonChanged;
        #endregion

        #region //----------------------------- Class Static Constructor ----------------------------------//
        static HexagonSlot()
        {
            BackgroundImageProperty = DependencyProperty.Register("BackgroundImage",
                typeof(Image), typeof(HexagonSlot), new PropertyMetadata(BackgroundImageProperty_Changed));

            HexagonProperty = DependencyProperty.Register("Hexagon",
                typeof(Hexagon), typeof(HexagonSlot), new PropertyMetadata(HexagonProperty_Changed));
        }
        #endregion

        #region //----------------------------- Class Public Constructors ---------------------------------//
        /// <summary>
        /// Khởi tạo 1 đối tượng HexagonSlot mới.
        /// </summary>
        public HexagonSlot()
        {
            InitializeComponent();

            //Uri uri = new Uri("TTApp.Phone.Silverlight;component/Assets/Images/Hexagon_Blue2.png", UriKind.Relative);
            //StreamResourceInfo resourceInfo = Application.GetResourceStream(uri);
            //BitmapImage bitmapImage = new BitmapImage();
            //bitmapImage.SetSource(resourceInfo.Stream);

            //BackgroundImage = new Image();
            //BackgroundImage.Source = bitmapImage;
            //BackgroundImage.Stretch = Stretch.Fill;

            _decorationDictionary = new Dictionary<string, Decoration>();

            _decorations = new DecorationCollection();
            _decorations.CollectionChanged += new NotifyCollectionChangedEventHandler(OnDecorationsCollectionChanged);
            _decorations.Add(new BackgroundDecoration("BackgroundDecoration"));
        }
        #endregion
        
        #region //----------------------------- Class CLR Properties --------------------------------------//
        /// <summary>
        /// Ảnh nền của HexagonSlot. Nằm ở dưới cùng (dưới Decorations và Hexagon).
        /// </summary>
        [TypeConverter(typeof(ImageConverter))]
        public Image BackgroundImage
        {
            get { return GetValue(BackgroundImageProperty) as Image; }
            set { SetValue(BackgroundImageProperty, value); }
        }

        /// <summary>
        /// Lấy hoặc gán Hexagon cho HexagonSlot này.
        /// </summary>
        public Hexagon Hexagon
        {
            get
            {
                return GetValue(HexagonProperty) as Hexagon;
            }
            set
            {
                SetValue(HexagonProperty, value);
                ReorderZIndex();
            }
        }

        /// <summary>
        /// Tập chứa các Decoration của HexagonSlot.
        /// </summary>
        public DecorationCollection Decorations
        {
            get { return _decorations; }
        }

        /// <summary>
        /// true nếu HexagonSlot này chưa chứa Hexagon.
        /// </summary>
        public bool IsEmpty
        {
            get { return Hexagon == null; }
        }
        #endregion

        #region //----------------------------- DP Changed Callbacks --------------------------------------//
        private static void BackgroundImageProperty_Changed(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            (obj as HexagonSlot).OnBackgroundImageChanged(args.OldValue as Image, args.NewValue as Image);
        }

        private static void HexagonProperty_Changed(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            (obj as HexagonSlot).OnHexagonChanged(args.OldValue as Hexagon, args.NewValue as Hexagon);
        }
        #endregion

        #region //----------------------------- Protected Event Invokers ----------------------------------//
        protected virtual void OnBackgroundImageChanged(Image oldValue, Image newValue)
        {
            Children.Remove(oldValue);
            Children.Add(newValue);

            Canvas.SetZIndex(newValue, 0);

            if (BackgroundImageChanged != null)
            {
                BackgroundImageChanged(this, new RoutedPropertyChangedEventArgs<Image>(oldValue, newValue));
            }
        }

        protected virtual void OnHexagonChanged(Hexagon oldValue, Hexagon newValue)
        {
            Children.Remove(oldValue);
            if (newValue != null)
            {
                Children.Add(newValue);
                Canvas.SetZIndex(newValue, 1);
            }

            if (HexagonChanged != null)
            {
                HexagonChanged(this, new RoutedPropertyChangedEventArgs<Hexagon>(oldValue, newValue));
            }
        }
        #endregion

        #region //----------------------------- Private Event Handlers ------------------------------------//
        private void OnDecorationsCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
        {
            if (args.Action == NotifyCollectionChangedAction.Add)
            {
                foreach (Decoration decoration in args.NewItems)
                {
                    this.Children.Add(decoration);
                    _decorationDictionary.Add(decoration.DecorationName, decoration);
                }
            }
            else if (args.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (Decoration decoration in args.OldItems)
                {
                    this.Children.Remove(decoration);
                    _decorationDictionary.Remove(decoration.DecorationName);
                }
            }
            else if (args.Action == NotifyCollectionChangedAction.Reset)
            {
                foreach (Decoration decoration in _decorations)
                {
                    Children.Remove(decoration);
                }
                _decorationDictionary.Clear();
            }

            ReorderZIndex();
        }
        #endregion

        #region //----------------------------- Class Public Methods --------------------------------------//
        /// <summary>
        /// Lấy hàng xóm cấp 1, tức là hàng xóm gần nhất của HexagonSlot này. Gọi phương thức này tương 
        /// đương với gọi GetNeighbours(1).
        /// </summary>
        /// <returns>List gồm 6 phần tử chứa hàng xóm theo thứ tự bắt đầu từ 12h và đi theo chiều
        /// thuận chiều kim đồng hồ.</returns>
        public List<HexagonSlot> GetNeighbours()
        {
            return GetNeighbours(1);
        }

        /// <summary>
        /// Lấy hàng xóm theo cấp độ (khoảng cách từ HexagonSlot hiện tại đến hàng xóm cần lấy).
        /// </summary>
        /// <param name="distance">Khoảng cách từ HexagonSlot hiện tại đến hàng xóm.</param>
        /// <returns>
        /// List gồm 6 * distance các HexagonSlot. Nếu không tồn tại hàng xóm ở vị trí nào đó thì
        /// hàng xóm đó lấy giá trị null.
        /// </returns>
        public List<HexagonSlot> GetNeighbours(int distance)
        {
            if (distance < 1)
            {
                throw new ArgumentOutOfRangeException("distance", "Khoảng cách phải lớn hơn hoặc bằng 1.");
            }

            // Tổng số các HexagonSlot hàng xóm.
            int total = 6 * distance;

            List<HexagonSlot> neighbours = new List<HexagonSlot>(total);
            for (int i = 0; i < total; i++)
            {
                neighbours.Add(null);
            }

            Board parent = VisualTreeHelper.GetParent(this) as Board;
            if (parent == null)
            {
                throw new InvalidOperationException("HexagonSlot chưa được đặt vào Board.");
            }

            double x = Board.GetX(this);
            double y = Board.GetY(this);

            for (int index = 0; index < total; index++)
            {
                neighbours[index] = parent.GetHexagonSlot(
                    GetXPosition(x, index, total),
                    GetYPosition(y, index, total));
            }

            return neighbours;
        }

        /// <summary>
        /// Lấy Decoration theo tên. Nếu tên không tìm thấy thì trả về null.
        /// </summary>
        /// <param name="decorationName">Tên của Decoration muốn lấy.</param>
        /// <returns>null nếu không tìm thấy Decoration.</returns>
        public Decoration GetDecoration(string decorationName)
        {
            Decoration result;
            _decorationDictionary.TryGetValue(decorationName, out result);

            return result;
        }

        /// <summary>
        /// Kiểm tra xem HexagonSlot này có phải là hàng xóm cấp distance so với Hexagon hexagon không.
        /// </summary>
        /// <param name="hexagon">Hexagon dùng để so sánh.</param>
        /// <param name="distance">Khoảng cách từ HexagonSlot này đến Hexagon hexagon.</param>
        /// <returns></returns>
        public bool IsNeighbourWith(Hexagon hexagon, int distance)
        {
            return hexagon.IsNeighbourWith(this, distance);
        }
        #endregion

        #region //----------------------------- Class Private Methods -------------------------------------//
        /// <summary>
        /// Lấy vị trí cột của HexgonSlot so với HexagonSlot ứng với cột origin.
        /// </summary>
        /// <param name="origin">Vị trí cột chọn làm gốc để tính.</param>
        /// <param name="index">
        /// Thứ tự của HexagonSlot trong tổng số total HexagonSlot hàng xóm.
        /// Thứ tự được tính từ góc 12 và đi theo chiều thuận chiều kim đồng hồ.
        /// </param>
        /// <param name="total">Tổng số các HexagonSlot hàng xóm.</param>
        /// <returns></returns>
        private double GetXPosition(double origin, int index, int total)
        {
            if (total % 6 != 0)
            {
                throw new ArgumentException("total", "Phải chia hết 6");
            }

            if (index < 0 || total <= index)
            {
                throw new ArgumentException("index", String.Format("Phải nằm trong [0, {0})",total));
            }

            // Tìm góc phần sáu (tương ứng với vị trí cạnh của Hexagon).
            int part = index / (total / 6);

            // Vị trí trong từng khoảng 1/6
            int pos = index % (total / 6);

            // Khoảng cách từ HexagonSlot đến hàng xóm
            int distance = total / 6;

            switch (part)
            {
                case 0: return origin + pos;
                case 1: return origin + distance;
                case 2: return origin + (distance - pos);
                case 3: return origin - pos;
                case 4: return origin - distance;
                case 5: return origin - (distance - pos);
            }

            // Should never reach here.
            return -1;
        }

        /// <summary>
        /// Lấy vị trí hàng của HexgonSlot so với HexagonSlot ứng với hàng origin.
        /// </summary>
        /// <param name="origin">Vị trí hàng chọn làm gốc để tính.</param>
        /// <param name="index">
        /// Thứ tự của HexagonSlot trong tổng số total HexagonSlot hàng xóm.
        /// Thứ tự được tính từ góc 12 và đi theo chiều thuận chiều kim đồng hồ.</param>
        /// <param name="total">Tổng số các HexagonSlot hàng xóm.</param>
        /// <returns></returns>
        private double GetYPosition(double origin, int index, int total)
        {
            if (total % 6 != 0)
            {
                throw new ArgumentException("total", "Phải chia hết 6");
            }

            if (index < 0 || total <= index)
            {
                throw new ArgumentException("index", String.Format("Phải nằm trong [0, {0})", total));
            }

            // Tìm góc phần sáu (tương ứng với vị trí cạnh của Hexagon).
            int part = index / (total / 6);

            // Vị trí trong từng khoảng 1/6
            int pos = index % (total / 6);

            // Khoảng cách từ HexagonSlot đến hàng xóm
            int distance = total / 6;

            switch (part)
            {
                case 0: return origin - distance + (0.5 * pos);
                case 1: return origin - (distance / 2.0) + pos;
                case 2: return origin + (distance / 2.0) + (0.5 * pos);
                case 3: return origin + distance - (0.5 * pos);
                case 4: return origin + (distance / 2.0) - pos;
                case 5: return origin - (distance / 2.0) - (0.5 * pos);
            }

            // Should never reach here.
            return -1;
        }
        #endregion

        #region //----------------------------- Class Protected Methods -----------------------------------//
        /// <summary>
        /// Sắp xếp lại thứ tự  theo trục Z của Hexagon (nếu có) và các Decoration (nếu có).
        /// Hexagon bao giờ cũng nằm trên cùng so với các Decoration.
        /// </summary>
        protected void ReorderZIndex()
        {
            for (int i = 0; i < _decorations.Count; i++)
            {
                Canvas.SetZIndex(_decorations[i], i);
            }

            if (Hexagon != null)
            {
                Canvas.SetZIndex(Hexagon, _decorations.Count);
            }
        }
        #endregion
    }
}